<template>
  <div class="yl-time-range">
    <table class="yl-time-range-table" @mouseleave="leaveTable">
      <!-- 表头 -->
      <thead>
        <tr>
          <!-- 第一列显示星期，并且跨2行 -->
          <th colspan="8" rowspan="2" class=".week-td">星期/时间</th>
          <th colspan="24">00:00 - 12:00</th>
          <th colspan="24">12:00 - 24:00</th>
        </tr>
        <tr>
          <!-- 时间整点显示，从0开始，跨2列 -->
          <td colspan="2" v-for="i in 24" :key="i">{{ i - 1 }}</td>
        </tr>
      </thead>
      <tbody>
        <!-- 表格列 -->
        <tr v-for="(week, weekIndex) in weekLabels" :key="week">
          <!-- 第一列显示星期 -->
          <td colspan="8" rowspan="1">{{ week }}</td>
          <!-- 后面的列 -->
          <td
            :class="{ 'yl-time-range-isactive': time === 1 }"
            colspan="1"
            v-for="(time, timeIndex) in innerValue[weekIndex]"
            :key="timeIndex"
            @mousedown="multiStart($event, weekIndex, timeIndex)"
            @mousemove="multiMove($event, weekIndex, timeIndex)"
            @mouseup="multiEnd($event, weekIndex, timeIndex)"
          >
            <!-- tooltip 显示时间 -->
            <v-popover
              offset="26"
              trigger="hover"
              :delay="{ show: 500, hide: 0 }"
              placement="top"
              popoverClass="yl-time-range-popover"
            >
              <div class="yl-time-range-triger"></div>
              <template slot="popover">
                {{ weekLabels[weekIndex] }}
                {{ labels[timeIndex][0] + " - " + labels[timeIndex][1] }}
              </template>
            </v-popover>
          </td>
        </tr>
      </tbody>
    </table>
    <!-- 选取遮罩层 -->
    <div
      class="yl-time-range-cover"
      v-show="cover.active && cover.moved"
      :style="coverStyle"
    ></div>
    <!-- 结果显示区域 -->
    <div class="yl-time-range-footer">
      <header class="yl-time-range-footer-title">
        <div>{{ hasSelected ? "已选择时间段" : "可拖动鼠标选择时间段" }}</div>
        <a href="javascript: void(0);" @click="initTime">清空</a>
      </header>
      <section class="yl-time-range-footer-result">
        <!-- 循环星期展示 -->
        <template v-for="(week, weekIndex) in weekLabels">
          <div
            class="yl-time-range-recode"
            :key="week"
            v-if="selectedResult[weekIndex]"
          >
            <span class="yl-time-range-recode-label">{{ week }}:</span>
            <span>{{ selectedResult[weekIndex] }}</span>
          </div>
        </template>
      </section>
    </div>
  </div>
</template>

<script>
import { deepCopy } from "@/utils/utils";
export default {
  name: "YlTimeRangePicker",

  components: {},

  model: {
    prop: "value",
    event: "change",
  },

  props: {
    value: Array,
  },

  data() {
    return {
      labels: [], // 时间 label，用于选择后展示在下方

      // 星期显示
      weekLabels: [
        "星期一",
        "星期二",
        "星期三",
        "星期四",
        "星期五",
        "星期六",
        "星期日",
      ],

      // 鼠标拉取遮罩数据
      cover: {
        active: false, // 拉选进行中
        moved: false, // 鼠标移动中
        // 开始点
        start: {
          top: 0, // 顶部页面距离
          left: 0, // 左边页面距离
          width: 0, // 格子宽度
          height: 0, // 格子高度
          week: 0, // 周下标
          time: 0, // 时间下标
        },
        // 结束点
        end: {
          top: 0,
          left: 0,
          width: 0,
          height: 0,
          week: 0,
          time: 0,
        },
      },
    };
  },

  computed: {
    // 组件内部使用的值
    innerValue() {
      return this.value || this.buildTimeTemplate();
    },
    // 拉取遮罩样样式
    coverStyle() {
      let top, left, width, height;
      let { start, end } = this.cover;
      top = start.top > end.top ? end.top : start.top;
      left = start.left > end.left ? end.left : start.left;
      width =
        start.left > end.left
          ? start.left - end.left + end.width
          : end.left - start.left + start.width;
      height =
        start.top > end.top
          ? start.top - end.top + end.height
          : end.top - start.top + start.height;
      return {
        top: top + "px",
        left: left + "px",
        width: width + "px",
        height: height + "px",
      };
    },
    // 已有时间被选中
    hasSelected() {
      let result = false;
      this.innerValue.forEach((item) => {
        item.forEach((item2) => {
          if (item2 === 1) {
            result = true;
          }
        });
      });

      return result;
    },
    // 已选时间的显示
    selectedResult() {
      let result = [];
      this.innerValue.forEach((week) => {
        let temp = [];
        week.forEach((time, timeIndex) => {
          // 默认先加一组
          if (temp.length === 0) {
            temp.push([]);
          }
          // 如果是 0 新开一组
          if (time === 0) {
            temp.push([]);
          } else {
            // 否则往最后一组加
            temp[temp.length - 1].push(timeIndex);
          }
        });
        // 去掉空组
        temp = temp.filter((item) => {
          return item.length > 0;
        });
        // 显示 label
        let labelStr = temp
          .map((item) => {
            let labels = this.labels;
            let startIndex = item[0];
            let endIndex = item[item.length - 1];
            let start = labels[startIndex][0];
            let end = labels[endIndex][1];
            return start + "~" + end;
          })
          .join("、");
        result.push(labelStr);
      });

      return result;
    },
  },

  watch: {
    value(newValue) {
      if (!newValue) {
        this.initTime();
      }
    },
  },

  created() {
    this.buildTimeLabels(); // 生成时间 label
    // 如果原来没值，初始化一个
    if (!this.value) {
      this.initTime();
    }
  },

  methods: {
    // 生成时间模板
    buildTimeTemplate() {
      let times = [];
      for (let w = 0; w < 7; w++) {
        let temp = [];
        for (let d = 0; d < 48; d++) {
          temp.push(0);
        }
        times.push(temp);
      }
      return times;
    },
    // 初始化时间
    initTime() {
      this.$emit("change", this.buildTimeTemplate());
    },
    // 生成时间 label
    buildTimeLabels() {
      let tempLabels = [];
      for (let i = 0; i <= 24; i++) {
        let timeStr = "00" + i.toString();
        timeStr = timeStr.slice(-2);
        tempLabels.push(timeStr + ":00");
        tempLabels.push(timeStr + ":30");
      }
      let labels = [];
      for (let i = 0; i < 48; i++) {
        labels.push([tempLabels[i], tempLabels[i + 1]]);
      }
      this.labels = labels;
    },
    // 多选开始
    multiStart(e, week, time) {
      this.cover.active = true;
      let dom = e.target;
      let rect = dom.getBoundingClientRect();
      // 设置起始点
      this.cover.start = {
        top: rect.top,
        left: rect.left,
        width: rect.width,
        height: rect.height,
        week,
        time,
      };
      // 设置终止点
      this.cover.end = {
        top: rect.top,
        left: rect.left,
        width: rect.width,
        height: rect.height,
        week,
        time,
      };
    },
    // 拖动鼠标
    multiMove(e, week, time) {
      if (this.cover.active) {
        this.cover.moved = true;
        let dom = e.target;
        let rect = dom.getBoundingClientRect();
        // 设置终止点
        this.cover.end = {
          top: rect.top,
          left: rect.left,
          width: rect.width,
          height: rect.height,
          week,
          time,
        };
      }
    },
    // 松开鼠标，结束选择
    multiEnd(e, week, time) {
      if (this.cover.active && this.cover.moved) {
        let dom = e.target;
        let rect = dom.getBoundingClientRect();
        // 设置终止点
        this.cover.end = {
          top: rect.top,
          left: rect.left,
          width: rect.width,
          height: rect.height,
          week,
          time,
        };
      }
      this.cover.active = false;
      this.cover.moved = false;
      this.toggleMultiChecked();
    },
    // 批量切换选中状态
    toggleMultiChecked() {
      let value = deepCopy(this.innerValue);
      let { start, end } = this.cover;
      let weekStart = start.week > end.week ? end.week : start.week;
      let weekEnd = start.week > end.week ? start.week : end.week;
      let timeStart = start.time > end.time ? end.time : start.time;
      let timeEnd = start.time > end.time ? start.time : end.time;
      let status = value[start.week][start.time];
      let result = status === 0 ? 1 : 0;
      for (let i = weekStart; i <= weekEnd; i++) {
        for (let j = timeStart; j <= timeEnd; j++) {
          value[i][j] = result;
        }
      }
      this.$emit("change", value);
    },
    // 离开表格，选择
    leaveTable() {
      if (this.cover.active && this.cover.moved) {
        this.toggleMultiChecked();
      }
      this.cover.active = false;
      this.cover.moved = false;
    },
  },
};
</script>

<style lang="less" scoped>
.yl-time-range {
  width: 100%;
  background: #fff;
}
.yl-time-range-table {
  border-collapse: collapse;
  border-radius: 4px;
  table-layout: fixed;
  width: 573px;
  user-select: none;

  tr,
  td,
  th {
    border: 1px solid #e4e9ed;
    font-size: 12px;
    text-align: center;
    min-width: 11px;
    line-height: 1.8em;
    transition: background 200ms ease;
    padding: 0;
    height: 28px;
  }

  .week-td {
    width: 75px;
    padding: 20px 0;
  }

  thead th,
  thead td {
    background: #f0f2f5;
  }

  tbody td {
    background: #fff;
    position: relative;

    &:hover {
      background: #f5f5f5;
    }

    &:first-child {
      background: #f0f2f5;
    }
    &.yl-time-range-isactive {
      background: @primary-color;
    }
  }
}

.yl-time-range-cover {
  background: #0864f7;
  width: 0;
  height: 0;
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  // transition: all 400ms ease;
  opacity: 0.6;
}

.yl-time-range-footer {
  width: 573px;
  padding: 0 12px;
  border: 1px solid #d9d9d9;
  margin-top: -1px;
  background: #fff;
}
.yl-time-range-footer-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 12px 0;
}
.yl-time-range-triger {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}
.yl-time-range-recode {
  margin: 12px 0;
}
.yl-time-range-recode-label {
  margin-right: 8px;
  color: #a2a2a2;
}
</style>

<style lang="less">
.yl-time-range-popover {
  pointer-events: none;
}
</style>
